#include "qinputhooks.h"
#include <qt_windows.h>
#include <QDebug>
#include <QMetaMethod>
#include <QTextCodec>
#include <iostream>

#include <wchar.h>

#define HKEY_ID(nativeShortcut) (((nativeShortcut.key ^ (nativeShortcut.modifier << 8)) & 0x0FFF) | 0x7000)


class WinEvent
{
public:
    WinEvent(int nCode, WPARAM wParam, LPARAM lParam)
    {
        this->nCode = nCode;
        this->wParam = wParam;
        this->lParam = lParam;
    }

    int nCode;
    WPARAM wParam;
    LPARAM lParam;
};

LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HC_ACTION) {
        switch (wParam) {
            // Pass KeyDown/KeyUp messages for Qt class to logicize
        case WM_KEYDOWN:
            //mwReference->keyDown(PKBDLLHOOKSTRUCT(lParam)->vkCode);
            QInputHooks::instance()->activateNativeKeyEvent(new WinEvent(nCode, wParam, lParam));
            break;
        case WM_KEYUP:
            //mwReference->keyUp(PKBDLLHOOKSTRUCT(lParam)->vkCode);
            QInputHooks::instance()->activateNativeKeyEvent(new WinEvent(nCode, wParam, lParam));
            break;
        default:
            qDebug()<<"wtfffffffffffff"<<nCode<<wParam;
        }
    }    else {
        qDebug()<<"wtf code"<<nCode<<qrand();
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

HHOOK hhkLowLevelKybd;
void QInputHooks::initNativeHook()
{
    hhkLowLevelKybd = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), 0);
    if (hhkLowLevelKybd == NULL) {
        qDebug()<<"error set hook:" << hhkLowLevelKybd << GetLastError();
    }

    if (false) {
        UnhookWindowsHookEx(hhkLowLevelKybd);
    }
}

void QInputHooks::activateNativeKeyEvent( void *nev )
{
    QMetaMethod signal = QMetaMethod::fromSignal(&QInputHooks::KeyDown);
    // signal.invoke(this, Qt::QueuedConnection);
    QKeyEvent e(QEvent::Type(0), 0, Qt::NoModifier);
    KeyEvent e_= nativeKeyEvent(nev);
    emit this->KeyDown(e_);
    KeyEventX ex = keyEventTox(e_);
    emit this->KeyDown2(ex);
    // emit this->KeyDown(QEvent::Type(0), 0, Qt::NoModifier);
}

#include "qintputhooks_win_keymap.cpp"

enum WindowsNativeModifiers {
    ShiftLeft            = 0x00000001,
    ControlLeft          = 0x00000002,
    AltLeft              = 0x00000004,
    MetaLeft             = 0x00000008,
    ShiftRight           = 0x00000010,
    ControlRight         = 0x00000020,
    AltRight             = 0x00000040,
    MetaRight            = 0x00000080,
    CapsLock             = 0x00000100,
    NumLock              = 0x00000200,
    ScrollLock           = 0x00000400,
    ExtendedKey          = 0x01000000,

    // Convenience mappings
    ShiftAny             = 0x00000011,
    ControlAny           = 0x00000022,
    AltAny               = 0x00000044,
    MetaAny              = 0x00000088,
    LockAny              = 0x00000700
};

KeyEvent QInputHooks::nativeKeyEvent( void *ev )
{
    WinEvent *nev = (WinEvent*)ev;
    QKeyEvent qev(QEvent::Type(0), 0, Qt::NoModifier);

    QEvent::Type qkeyty;
    int qkeycode;
    QString qkeytxt;
    int nativeScanCode;
    int nativeVirtualKey;
    int nativeModifiers;
    Qt::KeyboardModifiers qmods;

    if (nev->nCode != HC_ACTION) {
        return qev;
    }

    if (nev->wParam == WM_KEYDOWN) {
        qkeyty = QEvent::Type(QEvent::KeyPress);
    } else if (nev->wParam == WM_KEYUP) {
        qkeyty = QEvent::Type(QEvent::KeyRelease);
    }

    KBDLLHOOKSTRUCT *kbst = (KBDLLHOOKSTRUCT *)nev->lParam;
    nativeScanCode = kbst->scanCode;
    nativeVirtualKey = kbst->vkCode;
    qkeycode = nativeVirtualKey;
    if (qkeycode < Qt::Key_Space) {
        qkeycode = KeyTbl[kbst->vkCode];
    }
    // qDebug()<<"nsc:"<<nativeScanCode<<"nvc:"<<nativeVirtualKey<<"qkc"<<qkeycode;

    DWORD dwMsg = 1;
    dwMsg += kbst->scanCode << 16;
    dwMsg += kbst->flags << 24;
    wchar_t str[20]={0};
    auto nsz = GetKeyNameText( dwMsg, str, 20);
    qkeytxt = QString::fromWCharArray(str, nsz);
    auto err = GetLastError();
    // qDebug()<<nsz<<err<<str;

    {
        quint32 nModifiers = 0;

        // Map native modifiers to some bit representation
        nModifiers |= (GetKeyState(VK_LSHIFT  ) & 0x80 ? ShiftLeft : 0);
        nModifiers |= (GetKeyState(VK_RSHIFT  ) & 0x80 ? ShiftRight : 0);
        nModifiers |= (GetKeyState(VK_LCONTROL) & 0x80 ? ControlLeft : 0);
        nModifiers |= (GetKeyState(VK_RCONTROL) & 0x80 ? ControlRight : 0);
        nModifiers |= (GetKeyState(VK_LMENU   ) & 0x80 ? AltLeft : 0);
        nModifiers |= (GetKeyState(VK_RMENU   ) & 0x80 ? AltRight : 0);
        nModifiers |= (GetKeyState(VK_LWIN    ) & 0x80 ? MetaLeft : 0);
        nModifiers |= (GetKeyState(VK_RWIN    ) & 0x80 ? MetaRight : 0);
        // Add Lock keys to the same bits
        nModifiers |= (GetKeyState(VK_CAPITAL ) & 0x01 ? CapsLock : 0);
        nModifiers |= (GetKeyState(VK_NUMLOCK ) & 0x01 ? NumLock : 0);
        nModifiers |= (GetKeyState(VK_SCROLL  ) & 0x01 ? ScrollLock : 0);

        // Get the modifier states (may be altered later, depending on key code)
        int state = 0;
        state |= (nModifiers & ShiftAny ? int(Qt::ShiftModifier) : 0);
        state |= (nModifiers & ControlAny ? int(Qt::ControlModifier) : 0);
        state |= (nModifiers & AltAny ? int(Qt::AltModifier) : 0);
        state |= (nModifiers & MetaAny ? int(Qt::MetaModifier) : 0);

        qmods = Qt::KeyboardModifier(state);
        nativeModifiers = nModifiers;
    }

    qev = QKeyEvent(qkeyty, qkeycode, qmods,
                    nativeScanCode, nativeVirtualKey, nativeModifiers,
                    qkeytxt);
    // qDebug()<<qev.modifiers()<<qev.text()<<Qt::Key(qkeycode)<<qkeytxt<<int(Qt::Key_F);

    KeyEvent e_ = KeyEvent(qev);
    e_.time = kbst->time;
    auto epos = GetMessagePos();
    auto wid = GetActiveWindow();
    wid = GetForegroundWindow();
    e_.wid = (WId)wid;

    wchar_t wcls[256] = {0};
    auto clslen = GetClassName(wid, wcls, 128);
    // qDebug()<<clslen<<QString::fromWCharArray(wcls, clslen);
    wchar_t wtitle[GetWindowTextLength(wid)+1] = {0};
    auto titlen = GetWindowText(wid, wtitle, GetWindowTextLength(wid)+1);
    // qDebug()<<titlen<<QString::fromWCharArray(wtitle, titlen);
    e_.wtitle = QString::fromWCharArray(wtitle, titlen); // TODO encoding

    delete nev;
    return e_;
}




